<?php
namespace system\libraries;

use system\core\Registry;
use system\libraries\Translation;

/**
 * Form Validation
 * 
 * This library can handle form validation. It only supports the POST method.
 * 
 * @author Steffen Brem <steffenbrem@gmail.com>
 * @package System.Libraries
 */
class FormValidation 
{
    private $_fieldData = array();
    private $_errors;
    private $_group;
    
    /**
     * Stores the Translation instance
     *
     * @var System\Libraries\Translation
     */
    private $_translation;
    
    /**
     * Constructor
     *  
     * @param string $group
     */
    public function __construct( $group = 'default' )
    {
        $this->_group = $group;
        
        // We wanna load the form helper, so...
        require_once SYS_PATH . 'helpers/form_helper.php';
        
        // Load the form validation language file
        $this->_translation = new Translation();
        $this->_translation->load('form_validation');
    }
    
    /**
     * Set the rules for a form element
     *
     * @param string $field
     * @param string $label
     * @param string $rules
     */
    public function rules( $field, $label = '', $rules = NULL )
    {
        if ( count($_POST) == 0 )
        {
            return;
        }
        
        if ( ! is_string($field) || ! is_string($rules) )
        {
            return;
        }
        
        $this->_fieldData[$field] = array(
            'field' => $field,
            'label' => $label,
            'rules' => $rules,
            'post_data'  => NULL
        );
    }
    
    /**
     * Run the validation!
     * 
     * @return bool
     */
    public function run()
    {
        if ( count($_POST) == 0 )
        {
            return FALSE;
        }
        
        if ( ! isset($this->_fieldData) || count($this->_fieldData) == 0 )
        {
            return FALSE;
        }

        // Loop trough every form field
        foreach ( $this->_fieldData as $field => $data )
        {
            $postData = ( isset($_POST[$field]) ) ? $_POST[$field] : NULL;
            $this->_fieldData[$field]['post_data'] = $postData;
            
            $rules = explode('|', $data['rules']);

            // Loop trough all the rules of this field
            foreach ( $rules as $rule )
            {
                $param = NULL;
                $callback = FALSE;
                
                // Handle parameters, like min_length[3]
                if ( preg_match("/(.*?)\[(.*)\]/", $rule, $match) )
                {
                    $rule   = $match[1];
                    $param  = $match[2];
                }
                
                // Handle callbacks
                if ( preg_match("/(callback_\w+(\[.*?\])?)/", trim($rule), $match) )
                {
                    $callback = $rule;
                }
                
                if ( $callback )
                {
                    $instance = Registry::get('controller_instance', TRUE);
                    
                    if ( method_exists($instance, $callback) )
                    {
                        $result = $instance->$callback( $postData );
                    }
                    else
                    {
                        throw new \Exception('Could not execute callback. Method "' . $callback . '()" does not exist.');
                    }
                    
                    if ( $result !== TRUE )
                    {
                        // Callback returned an error, lets translate!
                        $error = $this->_translate($result);
                        $label = $this->_translate($data['label']);
                        
                        $this->_errors[$this->_group][$field] = sprintf($error, $label);
                        break;
                    }
                }
                else
                {
                    if ( ! method_exists($this, $rule) )
                    {
                        throw new \Exception('The form validation rule "' . $rule . '" does not exist.');
                    }

                    if ( $this->$rule($postData, $param) !== TRUE )
                    {
                        $label = $this->_translate($data['label']);
                        
                        // Translate parameter if its a field
                        if ( isset($this->_fieldData[$param]) )
                        {
                            $param = $this->_translate($this->_fieldData[$param]['label']);
                        }
                        
                        $this->_errors[$this->_group][$field] = sprintf($this->_translation->line($rule), $label, $param );
                        break;
                    }
                }
            }
        }
        
        // No errors? Validation is OK!
        if ( ! $this->_errors )
        {
            return TRUE;
        }
        
        // Get previous validation errors, so we do not overwrite them
        $errors = Registry::get('form_validation_errors');
        if ( is_array($errors) )
        {
            foreach ( $errors as $group => $value )
            {
                $this->_errors[$group] = $value;
            }
        }
        
        Registry::set('form_validation_errors', $this->_errors);
        
        return FALSE;
    }
    
    /**
     * Check for translation. If your string contains 'lang:required', it will
     * fetch the language line 'required' and return it.
     * 
     * @param string &$str reference
     * @return string
     */
    private function _translate( $str )
    {
        if ( substr($str, 0, 5) == 'lang:' )
        {
            $str = substr($str, 5);
            $str = $this->_translation->line($str);
        }
        
        return $str;
    }
    
    public function required( $str )
    {
        return ( strlen($str) > 0 ) ? TRUE : FALSE;
    }
    
    public function matches( $str, $field )
    {
        return ( $str == @$_POST[$field] ) ? TRUE : FALSE;
    }
    
    public function regex( $str, $regex )
    {
        return ( preg_match($regex, $str) ) ? TRUE : FALSE;
    }
    
    public function is_email( $str )
    {
        return ( ! preg_match("/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/ix", $str)) ? FALSE : TRUE;
    }
    
    public function min_length( $str, $length )
    {
        return ( strlen($str) >= $length ) ? TRUE : FALSE;
    }
    
    public function max_length( $str, $length )
    {
        return ( strlen($str) <= $length ) ? TRUE : FALSE;
    }
    
    public function exact_length( $str, $length )
    {
        return ( strlen($str) == $length ) ? TRUE : FALSE;
    }
    
    public function less_than( $str, $number )
    {
        return ( $str < $number && is_numeric($str) ) ? TRUE : FALSE;
    }
    
    public function greater_than( $str, $number )
    {
        return ( $str > $number && is_numeric($str) ) ? TRUE : FALSE;
    }
    
    public function alpha ( $str )
    {
        return ( ! preg_match("/^([a-z])+$/i", $str) ) ? FALSE : TRUE;
    }
    
    public function alpha_numeric( $str )
    {
        return ( ! preg_match("/^([a-z0-9])+$/i", $str) ) ? FALSE : TRUE;
    }
    
    public function alpha_numeric_dash( $str )
    {
        return ( ! preg_match("/^([-a-z0-9_-])+$/i", $str) ) ? FALSE : TRUE;
    }
    
    public function numeric( $str )
    {
        return ( preg_match( '/^[\-+]?[0-9]*\.?[0-9]+$/', $str) ) ? TRUE : FALSE;
    }
    
    public function integer( $str )
    {
        return ( preg_match('/^[\-+]?[0-9]+$/', $str) ) ? TRUE : FALSE;
    }
    
    public function decimal( $str )
    {
        return ( preg_match('/^[\-+]?[0-9]+\.[0-9]+$/', $str) ) ? TRUE : FALSE;
    }
    
}

/* End of file FormValidation.php */
